// ********************************************************************** // // Copyright (c) 2003-2010 ZeroC, Inc. All rights reserved. // // This copy of Ice is licensed to you under the terms described in the // ICE_LICENSE file included in this distribution. // // ********************************************************************** package IceSSL; class TrustManager { TrustManager(Ice.Communicator communicator) { assert communicator != null; _communicator = communicator; Ice.Properties properties = communicator.getProperties(); _traceLevel = properties.getPropertyAsInt("IceSSL.Trace.Security"); String key = null; try { key = "IceSSL.TrustOnly"; parse(properties.getProperty(key), _rejectAll, _acceptAll); key = "IceSSL.TrustOnly.Client"; parse(properties.getProperty(key), _rejectClient, _acceptClient); key = "IceSSL.TrustOnly.Server"; parse(properties.getProperty(key), _rejectAllServer, _acceptAllServer); java.util.Map<String, String> dict = properties.getPropertiesForPrefix("IceSSL.TrustOnly.Server."); for(java.util.Map.Entry<String, String> p : dict.entrySet()) { key = p.getKey(); String name = key.substring("IceSSL.TrustOnly.Server.".length()); java.util.List<java.util.List<RFC2253.RDNPair> > reject = new java.util.LinkedList<java.util.List<RFC2253.RDNPair> >(); java.util.List<java.util.List<RFC2253.RDNPair> > accept = new java.util.LinkedList<java.util.List<RFC2253.RDNPair> >(); parse(p.getValue(), reject, accept); if(!reject.isEmpty()) { _rejectServer.put(name, reject); } if(!accept.isEmpty()) { _acceptServer.put(name, accept); } } } catch(RFC2253.ParseException e) { Ice.PluginInitializationException ex = new Ice.PluginInitializationException(); ex.reason = "IceSSL: invalid property " + key + ":\n" + e.reason; throw ex; } } boolean verify(NativeConnectionInfo info) { java.util.List<java.util.List<java.util.List<RFC2253.RDNPair> > > reject = new java.util.LinkedList<java.util.List<java.util.List<RFC2253.RDNPair> > >(), accept = new java.util.LinkedList<java.util.List<java.util.List<RFC2253.RDNPair> > >(); if(!_rejectAll.isEmpty()) { reject.add(_rejectAll); } if(info.incoming) { if(!_rejectAllServer.isEmpty()) { reject.add(_rejectAllServer); } if(info.adapterName.length() > 0) { java.util.List<java.util.List<RFC2253.RDNPair> > p = _rejectServer.get(info.adapterName); if(p != null) { reject.add(p); } } } else { if(!_rejectClient.isEmpty()) { reject.add(_rejectClient); } } if(!_acceptAll.isEmpty()) { accept.add(_acceptAll); } if(info.incoming) { if(!_acceptAllServer.isEmpty()) { accept.add(_acceptAllServer); } if(info.adapterName.length() > 0) { java.util.List<java.util.List<RFC2253.RDNPair> > p = _acceptServer.get(info.adapterName); if(p != null) { accept.add(p); } } } else { if(!_acceptClient.isEmpty()) { accept.add(_acceptClient); } } // // If there is nothing to match against, then we accept the cert. // if(reject.isEmpty() && accept.isEmpty()) { return true; } // // If there is no certificate then we match false. // if(info.nativeCerts != null && info.nativeCerts.length > 0) { javax.security.auth.x500.X500Principal subjectDN = (javax.security.auth.x500.X500Principal) ((java.security.cert.X509Certificate)info.nativeCerts[0]).getSubjectX500Principal(); String subjectName = subjectDN.getName(javax.security.auth.x500.X500Principal.RFC2253); assert subjectName != null; try { // // Decompose the subject DN into the RDNs. // if(_traceLevel > 0) { if(info.incoming) { _communicator.getLogger().trace("Security", "trust manager evaluating client:\n" + "subject = " + subjectName + "\n" + "adapter = " + info.adapterName + "\n" + "local addr = " + info.localAddress + ":" + info.localPort + "\n" + "remote addr = " + info.remoteAddress + ":" + info.remotePort); } else { _communicator.getLogger().trace("Security", "trust manager evaluating server:\n" + "subject = " + subjectName + "\n" + "local addr = " + info.localAddress + ":" + info.localPort + "\n" + "remote addr = " + info.remoteAddress + ":" + info.remotePort); } } java.util.List<RFC2253.RDNPair> dn = RFC2253.parseStrict(subjectName); // // Fail if we match anything in the reject set. // for(java.util.List<java.util.List<RFC2253.RDNPair>> matchSet : reject) { if(_traceLevel > 1) { StringBuilder s = new StringBuilder("trust manager rejecting PDNs:\n"); stringify(matchSet, s); _communicator.getLogger().trace("Security", s.toString()); } if(match(matchSet, dn)) { return false; } } // // Succeed if we match anything in the accept set. // for(java.util.List<java.util.List<RFC2253.RDNPair>> matchSet : accept) { if(_traceLevel > 1) { StringBuilder s = new StringBuilder("trust manager accepting PDNs:\n"); stringify(matchSet, s); _communicator.getLogger().trace("Security", s.toString()); } if(match(matchSet, dn)) { return true; } } } catch(RFC2253.ParseException e) { _communicator.getLogger().warning( "IceSSL: unable to parse certificate DN `" + subjectName + "'\nreason: " + e.reason); } // // At this point we accept the connection if there are no explicit accept rules. // return accept.isEmpty(); } return false; } private boolean match(java.util.List<java.util.List<RFC2253.RDNPair> > matchSet, java.util.List<RFC2253.RDNPair> subject) { for(java.util.List<RFC2253.RDNPair> r : matchSet) { if(matchRDNs(r, subject)) { return true; } } return false; } private boolean matchRDNs(java.util.List<RFC2253.RDNPair> match, java.util.List<RFC2253.RDNPair> subject) { for(RFC2253.RDNPair matchRDN : match) { boolean found = false; for(RFC2253.RDNPair subjectRDN : subject) { if(matchRDN.key.equals(subjectRDN.key)) { found = true; if(!matchRDN.value.equals(subjectRDN.value)) { return false; } } } if(!found) { return false; } } return true; } void parse(String value, java.util.List<java.util.List<RFC2253.RDNPair> > reject, java.util.List<java.util.List<RFC2253.RDNPair> > accept) throws RFC2253.ParseException { // // Java X500Principal.getName says: // // If "RFC2253" is specified as the format, this method emits // the attribute type keywords defined in RFC 2253 (CN, L, ST, // O, OU, C, STREET, DC, UID). Any other attribute type is // emitted as an OID. Under a strict reading, RFC 2253 only // specifies a UTF-8 string representation. The String // returned by this method is the Unicode string achieved by // decoding this UTF-8 representation. // // This means that things like emailAddress and such will be turned into // something like: // // 1.2.840.113549.1.9.1=#160e696e666f407a65726f632e636f6d // // The left hand side is the OID (see // http://www.columbia.edu/~ariel/ssleay/asn1-oids.html) for a // list. The right hand side is a BER encoding of the value. // // This means that the user input, unless it uses the // unfriendly OID format, will not directly match the // principal. // // Two possible solutions: // // Have the RFC2253 parser convert anything that is not CN, L, // ST, O, OU, C, STREET, DC, UID into OID format, and have it // convert the values into a BER encoding. // // Send the user data through X500Principal to string form and // then through the RFC2253 encoder. This uses the // X500Principal to do the encoding for us. // // The latter is much simpler, however, it means we need to // send the data through the parser twice because we split the // DNs on ';' which cannot be blindly split because of quotes, // \ and such. // java.util.List<RFC2253.RDNEntry> l = RFC2253.parse(value); for(RFC2253.RDNEntry e : l) { StringBuilder v = new StringBuilder(); boolean first = true; for(RFC2253.RDNPair pair : e.rdn) { if(!first) { v.append(","); } first = false; v.append(pair.key); v.append("="); v.append(pair.value); } javax.security.auth.x500.X500Principal princ = new javax.security.auth.x500.X500Principal(v.toString()); String subjectName = princ.getName(javax.security.auth.x500.X500Principal.RFC2253); if(e.negate) { reject.add(RFC2253.parseStrict(subjectName)); } else { accept.add(RFC2253.parseStrict(subjectName)); } } } private static void stringify(java.util.List<java.util.List<RFC2253.RDNPair>> matchSet, StringBuilder s) { boolean addSemi = false; for(java.util.List<RFC2253.RDNPair> rdnSet : matchSet) { if(addSemi) { s.append(';'); } addSemi = true; boolean addComma = false; for(RFC2253.RDNPair rdn : rdnSet) { if(addComma) { s.append(','); } addComma = true; s.append(rdn.key); s.append('='); s.append(rdn.value); } } } private Ice.Communicator _communicator; private int _traceLevel; private java.util.List<java.util.List<RFC2253.RDNPair> > _rejectAll = new java.util.LinkedList<java.util.List<RFC2253.RDNPair> >(); private java.util.List<java.util.List<RFC2253.RDNPair> > _rejectClient = new java.util.LinkedList<java.util.List<RFC2253.RDNPair> >(); private java.util.List<java.util.List<RFC2253.RDNPair> > _rejectAllServer = new java.util.LinkedList<java.util.List<RFC2253.RDNPair> >(); private java.util.Map<String, java.util.List<java.util.List<RFC2253.RDNPair> > > _rejectServer = new java.util.HashMap<String, java.util.List<java.util.List<RFC2253.RDNPair> > >(); private java.util.List<java.util.List<RFC2253.RDNPair> > _acceptAll = new java.util.LinkedList<java.util.List<RFC2253.RDNPair> >(); private java.util.List<java.util.List<RFC2253.RDNPair> > _acceptClient = new java.util.LinkedList<java.util.List<RFC2253.RDNPair> >(); private java.util.List<java.util.List<RFC2253.RDNPair> > _acceptAllServer = new java.util.LinkedList<java.util.List<RFC2253.RDNPair> >(); private java.util.Map<String, java.util.List<java.util.List<RFC2253.RDNPair> > > _acceptServer = new java.util.HashMap<String, java.util.List<java.util.List<RFC2253.RDNPair> > >(); }